/**
 * 
 */
package com.feizhi.common.collection;

import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author shuaqiu 2013年12月14日
 *
 */
public class LimitSet<T> extends AbstractSet<T> implements Set<T> {
    /**    */
    private static final int MAX_INIT_SIZE = 128;
    /** 最大的容量, 超过这个容量, 则不能再添加元素了 */
    private final int maxSize;
    /** 如果一个元素超过这个时间没有被使用, 则从cache 中删除. 默认是30 分钟 */
    private long maxIdleTime = TimeUnit.MINUTES.toMillis(30);

    /** 保存容器中的对象 --> 容器中对象的最后访问时间 */
    private final ConcurrentMap<T, Long> cache;

    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 
     */
    public LimitSet(final int maxSize) {
        this.maxSize = maxSize;

        final int initSize = getInitSize(maxSize);
        cache = new ConcurrentHashMap<T, Long>(initSize);
    }

    /**
     * @param maxSize
     * @return
     */
    private int getInitSize(final int maxSize) {
        if (maxSize < MAX_INIT_SIZE) {
            return maxSize;
        }
        return MAX_INIT_SIZE;
    }

    /**
     * @param maxIdleTime
     *            the maxIdleTime to set
     */
    public void setMaxIdleTime(final long maxIdleTime) {
        this.maxIdleTime = maxIdleTime;
    }

    @Override
    public Iterator<T> iterator() {
        return cache.keySet().iterator();
    }

    @Override
    public int size() {
        return cache.size();
    }

    @Override
    public boolean isEmpty() {
        return cache.isEmpty();
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean contains(final Object value) {
        final Long lastAccessTime = cache.get(value);
        if (lastAccessTime == null) {
            return false;
        }

        final long current = System.currentTimeMillis();
        if (current - lastAccessTime > maxIdleTime) {
            cache.remove(value);
            return false;
        }
        // 更新访问时间
        cache.put((T) value, current);
        return true;
    }

    @Override
    public boolean add(final T value) {
        if (contains(value)) {
            return true;
        }
        if (checkSize()) {
            cache.put(value, System.currentTimeMillis());
            return true;
        }
        return false;
    }

    /**
     * @return
     */
    private boolean checkSize() {
        removeIdle();
        if (cache.size() < maxSize) {
            return true;
        }

        return false;
    }

    /**
     * 
     */
    private void removeIdle() {
        final long current = System.currentTimeMillis();

        lock.writeLock().lock();
        try{
            final Set<Entry<T, Long>> entrySet = cache.entrySet();
            final Iterator<Entry<T, Long>> iterator = entrySet.iterator();
            while (iterator.hasNext()) {
                final Entry<T, Long> entry = iterator.next();
                if (current - entry.getValue() > maxIdleTime) {
                    iterator.remove();
                }
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public boolean remove(final Object key) {
        final Long lastAccessTime = cache.remove(key);
        if (lastAccessTime == null) {
            return false;
        }

        final long current = System.currentTimeMillis();
        if (current - lastAccessTime > maxIdleTime) {
            return false;
        }
        return true;
    }

    @Override
    public void clear() {
        cache.clear();
    }
}
